package com.fiberhome.nas.core.util.file

import com.fiberhome.nas.core.util.log.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.RandomAccessFile
import java.security.MessageDigest
import java.text.DecimalFormat
import kotlin.math.log10
import kotlin.math.pow

/**
 * 文件工具类
 * 提供文件操作、MD5计算等功能
 */
object FileUtils {
    private const val TAG = "FileUtils"
    private const val BUFFER_SIZE = 8192
    private const val LARGE_FILE_THRESHOLD = 100 * 1024 * 1024 // 100MB
    private val decimalFormat = DecimalFormat("#.##")

    /**
     * 计算文件的MD5值（标准版）
     *
     * @param file 要计算MD5的文件
     * @return 文件的MD5值，如果计算失败则返回null
     */
    fun calculateMD5(file: File): String? {
        if (!file.exists() || !file.isFile) {
            return null
        }

        var fileInputStream: FileInputStream? = null
        try {
            val messageDigest = MessageDigest.getInstance("MD5")
            fileInputStream = FileInputStream(file)
            val buffer = ByteArray(BUFFER_SIZE)
            var bytesRead = 0

            while (fileInputStream.read(buffer).also { bytesRead = it } != -1) {
                messageDigest.update(buffer, 0, bytesRead)
            }

            val bytes = messageDigest.digest()
            return bytes.joinToString("") { "%02x".format(it) }
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            try {
                fileInputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 使用协程计算文件MD5，提高性能（和标准版结果一致）
     * 通过生产者-消费者模式并行处理文件读取和MD5计算
     *
     * @param file 要计算MD5的文件
     * @return 文件的MD5值
     */
    suspend fun calculateMD5Coroutine(file: File): String = coroutineScope {
        val digest = MessageDigest.getInstance("MD5")
        val channel = Channel<ByteArray>(capacity = 32)  // 缓冲队列

        // 生产者协程：异步读取文件块
        val producer = launch(Dispatchers.IO) {
            file.inputStream().use { input ->
                val buffer = ByteArray(1024 * 1024)  // 1MB 分块
                var bytesRead = 0
                while (input.read(buffer).also { bytesRead = it } != -1) {
                    val block = buffer.copyOf(bytesRead)  // 复制有效数据
                    channel.send(block)
                }
                channel.close()
            }
        }

        // 消费者协程：顺序更新摘要
        val consumer = launch(Dispatchers.Default) {
            for (block in channel) {
                synchronized(digest) {  // 线程安全更新
                    digest.update(block)
                }
            }
        }

        producer.join()
        consumer.join()
        bytesToHex(digest.digest())

    }

    /**
     * 优化的MD5计算方法，适用于大文件（结果会与标准版不一致）
     * 对于大文件，采用采样方式计算MD5，提高性能
     * 采样策略：取文件头部8MB、中间8MB和尾部8MB进行MD5计算
     *
     * @param file 要计算MD5的文件
     * @return 文件的MD5值，如果计算失败则返回null
     */
    fun calculateOptimizedMD5(file: File): String? {
        if (!file.exists() || !file.isFile) {
            return null
        }

        // 如果文件小于阈值，直接使用标准MD5计算
        if (file.length() < LARGE_FILE_THRESHOLD) {
            return calculateMD5(file)
        }

        var randomAccessFile: RandomAccessFile? = null
        try {
            val messageDigest = MessageDigest.getInstance("MD5")
            randomAccessFile = RandomAccessFile(file, "r")
            val fileSize = file.length()
            val buffer = ByteArray(BUFFER_SIZE)

            // 添加文件大小信息到MD5计算中，确保唯一性
            messageDigest.update(fileSize.toString().toByteArray())

            // 读取文件头部8MB
            val headSize = minOf(8 * 1024 * 1024, fileSize / 3)
            var bytesRead = 0
            var totalRead: Long = 0

            while (totalRead < headSize &&
                randomAccessFile.read(buffer, 0, minOf(BUFFER_SIZE, (headSize - totalRead).toInt()))
                    .also { bytesRead = it } != -1
            ) {
                messageDigest.update(buffer, 0, bytesRead)
                totalRead += bytesRead
            }

            // 读取文件中间部分8MB
            if (fileSize > headSize * 2) {
                val middleOffset = fileSize / 2 - headSize / 2
                randomAccessFile.seek(middleOffset)
                totalRead = 0L

                while (totalRead < headSize &&
                    randomAccessFile.read(
                        buffer,
                        0,
                        minOf(BUFFER_SIZE, (headSize - totalRead).toInt())
                    )
                        .also { bytesRead = it } != -1
                ) {
                    messageDigest.update(buffer, 0, bytesRead)
                    totalRead += bytesRead
                }
            }

            // 读取文件尾部8MB
            if (fileSize > headSize) {
                val tailOffset = maxOf(fileSize - headSize, headSize * 2)
                randomAccessFile.seek(tailOffset)
                totalRead = 0L

                while (totalRead < headSize &&
                    randomAccessFile.read(
                        buffer,
                        0,
                        minOf(BUFFER_SIZE, (headSize - totalRead).toInt())
                    )
                        .also { bytesRead = it } != -1
                ) {
                    messageDigest.update(buffer, 0, bytesRead)
                    totalRead += bytesRead
                }
            }

            val bytes = messageDigest.digest()
            return bytes.joinToString("") { "%02x".format(it) }
        } catch (e: Exception) {
            LogUtils.e(TAG, "Failed to calculate optimized MD5: ${e.message}")
            e.printStackTrace()
            return null
        } finally {
            try {
                randomAccessFile?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 计算文件片段的MD5值
     *
     * @param file 文件
     * @param offset 偏移量
     * @param length 长度
     * @return 文件片段的MD5值，如果计算失败则返回null
     */
    fun calculateChunkMD5(file: File, offset: Long, length: Long): String? {
        if (!file.exists() || !file.isFile) {
            return null
        }

        var fileInputStream: FileInputStream? = null
        try {
            val messageDigest = MessageDigest.getInstance("MD5")
            fileInputStream = FileInputStream(file)
            fileInputStream.skip(offset)

            val buffer = ByteArray(BUFFER_SIZE)
            var bytesRead = 0
            var totalBytesRead: Long = 0

            while (totalBytesRead < length &&
                fileInputStream.read(
                    buffer,
                    0,
                    minOf(BUFFER_SIZE, (length - totalBytesRead).toInt())
                )
                    .also { bytesRead = it } != -1
            ) {
                messageDigest.update(buffer, 0, bytesRead)
                totalBytesRead += bytesRead
            }

            val bytes = messageDigest.digest()
            return bytes.joinToString("") { "%02x".format(it) }
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            try {
                fileInputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 格式化文件大小
     *
     * @param size 文件大小（字节）
     * @return 格式化后的文件大小字符串
     */
    fun formatFileSize(size: Long): String {
        if (size <= 0) return "0 B"
        val units = arrayOf("B", "KB", "MB", "GB", "TB")
        val digitGroups = (log10(size.toDouble()) / log10(1024.0)).toInt()
        return "${decimalFormat.format(size / 1024.0.pow(digitGroups.toDouble()))} ${units[digitGroups]}"
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName 文件名
     * @return 文件扩展名，如果没有扩展名则返回空字符串
     */
    fun getFileExtension(fileName: String): String {
        val dotIndex = fileName.lastIndexOf('.')
        return if (dotIndex > 0 && dotIndex < fileName.length - 1) {
            fileName.substring(dotIndex + 1)
        } else {
            ""
        }
    }

    /**
     * 将字节数组转换为十六进制字符串
     */
    private fun bytesToHex(bytes: ByteArray): String {
        return bytes.joinToString("") { "%02x".format(it) }
    }

    /**
     * 获取文件名（不含扩展名）
     *
     * @param fileName 文件名
     * @return 不含扩展名的文件名
     */
    fun getFileNameWithoutExtension(fileName: String): String {
        val dotIndex = fileName.lastIndexOf('.')
        return if (dotIndex > 0) {
            fileName.substring(0, dotIndex)
        } else {
            fileName
        }
    }

    /**
     * 创建文件分片
     *
     * @param sourceFile 源文件
     * @param offset 分片起始位置
     * @param size 分片大小
     * @param outputFile 输出文件
     * @return 是否成功创建分片
     */
    fun createChunkFile(sourceFile: File, offset: Long, size: Long, outputFile: File): Boolean {
        if (!sourceFile.exists() || !sourceFile.isFile) {
            LogUtils.e(
                TAG,
                "Source file does not exist or is not a file: ${sourceFile.absolutePath}"
            )
            return false
        }

        return try {
            val randomAccessFile = RandomAccessFile(sourceFile, "r")
            val outputStream = outputFile.outputStream()

            randomAccessFile.seek(offset)
            val buffer = ByteArray(BUFFER_SIZE)
            var bytesRead: Int = 0
            var totalBytesRead: Long = 0

            while (totalBytesRead < size &&
                randomAccessFile.read(
                    buffer,
                    0,
                    minOf(buffer.size, (size - totalBytesRead).toInt())
                )
                    .also { bytesRead = it } != -1
            ) {
                outputStream.write(buffer, 0, bytesRead)
                totalBytesRead += bytesRead
            }

            outputStream.close()
            randomAccessFile.close()

            true
        } catch (e: Exception) {
            LogUtils.e(TAG, "Failed to create chunk file: ${e.message}")
            false
        }
    }

    /**
     * 创建临时文件分片
     *
     * @param sourceFile 源文件
     * @param offset 分片起始位置
     * @param size 分片大小
     * @param cacheDir 缓存目录
     * @return 临时文件，如果创建失败则返回null
     */
    fun createTempChunkFile(sourceFile: File, offset: Long, size: Long, cacheDir: File): File? {
        try {
            val tempFile = File.createTempFile("chunk_", ".tmp", cacheDir)
            if (createChunkFile(sourceFile, offset, size, tempFile)) {
                return tempFile
            }
            tempFile.delete()
            return null
        } catch (e: Exception) {
            LogUtils.e(TAG, "Failed to create temp chunk file: ${e.message}")
            return null
        }
    }
}